using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using System;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class EntityAutoNavigationComponent : TReusableEvtComponent
    {
        private AutoResetUniTaskCompletionSource<bool> _completeTaskSource;
        private int _navPathCount;
        private int _nextPosIndex = -1;
        private Vector3 _finalPos;
        private float[] _navPath;
        private Vector3 _nextPosInPath;
        private EntityMoveComponent _moveCom;
        private Vector3 _lastPosInPath;
        private Vector3 _toNextPosDir;

        public bool isNavigating => _nextPosIndex != -1;

        public override uint MaxStore => 100;

        public override void OnCreate(TEntity entity)
        {
            base.OnCreate(entity);
            _moveCom = _entity.GetComponent<EntityMoveComponent>(ETComponentType.EntityMove);
        }

        public async UniTask<bool> NavigateTo(Vector3 targetPos)
        {
            if (_moveCom == null)
            {
                LogGame.LogError($"导航失败，尝试导航发现实体缺少移动组件。");
                return false;
            }
            _completeTaskSource = AutoResetUniTaskCompletionSource<bool>.Create();
            _navPathCount = _entity.navigationCtrl.FindPath(_entity.unityPosOfData, targetPos, out _navPath);
            if (_navPathCount <= 1)
            {
                LogGame.LogError($"导航失败，找不到路径，目标点：{targetPos}。");
                Stop();
                return false;
            }
            _finalPos = targetPos;
            _nextPosIndex = 0;
            SwitchToNextPos();

            try
            {
                return await _completeTaskSource.Task;
            }
            catch (OperationCanceledException)
            {
                return false;
            }
            catch (Exception e)
            {
                LogGame.LogException(e);
                return false;
            }
        }

        private void SwitchToNextPos()
        {
            _nextPosIndex++;
            if (_nextPosIndex >= _navPathCount)
            {
                Stop();
                return;
            }
            _lastPosInPath = GetPosByPathIndex(_nextPosIndex - 1);
            _nextPosInPath = GetPosByPathIndex(_nextPosIndex);
            _toNextPosDir = (_nextPosInPath - _lastPosInPath).normalized;
        }

        private Vector3 GetPosByPathIndex(int index)
        {
            return new Vector3(-_navPath[index * 3], _navPath[index * 3 + 1], _navPath[index * 3 + 2]);
        }

        public override void OnUpdate(float deltaTime)
        {
            if (_nextPosIndex != -1)
            {
                float step = _entity.defaultMeterSpeed * deltaTime;

                //当前拟停下的位置
                Vector3 tryStopPos = _entity.unityPosOfData + _toNextPosDir * step;
                //当前目标点->最终目标点的矢量
                Vector3 curStopPosToEndPos = _nextPosInPath - tryStopPos;
                var autoMoveUnnormalizedDir = _nextPosInPath - _lastPosInPath;
                //如果上述矢量和前进方向基本一致，则保持前进，否则（说明已经到达或越过终点）停下
                if (Vector3.Dot(curStopPosToEndPos, autoMoveUnnormalizedDir) <= 0)
                {
                    SwitchToNextPos();
                }
                else
                {
                    MoveTo(tryStopPos);
                }
            }
        }

        private void MoveTo(Vector3 pos)
        {
            _moveCom?.MoveStep(pos, 1, true);
        }

        public void Stop(bool force = false)
        {
            if (!isNavigating)
            {
                return;
            }
            _moveCom?.MoveStep(_finalPos, 0, false);
            ClearNavData();
            CallTaskComplete(!force);
        }

        public void ForceEndTask()
        {
            CallTaskComplete(false);
        }

        private void CallTaskComplete(bool isFinished)
        {
            if (_completeTaskSource != null)
            {
                // 注意！这里要先置空，再调用，TrySetResult会先调用await之后的代码，如果await之后有给TaskSource赋新值，然后这里再置空，那么_completeActTaskSource就永远为null了
                var ts = _completeTaskSource;
                _completeTaskSource = null;
                ts.TrySetResult(isFinished);
            }
        }

        private void ClearNavData()
        {
            _nextPosIndex = -1;
            _nextPosInPath = default;
            _navPathCount = 0;
            _navPath = null;
            _finalPos = default;
            _toNextPosDir = default;
            _lastPosInPath = default;
        }

        public override void OnPoolReset()
        {
            ClearNavData();
            _moveCom = null;
            CallTaskComplete(false);
        }
    }
}
